//
//  command.c
//  usb2wireless
//
//  Created by alivehex on 14-10-27.
//
//

#include "command.h"
#include "uart.h"
#include "crc7.h"
#include "hardware.h"
#include "serial.h"
#include "ircode.h"

#include <string.h>

#define wait4sync()		pkg->state = PROC_STATE_SYNC0

extern uint8_t Temperature[2];
static int processCommand(uint8_t index, uint8_t *input, uint8_t *output, uint8_t data_len);

//
// Stream format:
// command index + number of data + data x n + crc7
//
void processCommandStream(proc_package_t *pkg) {
	uint8_t count;
	uint8_t buffer;
	uint8_t sync[2] = {PROC_STATE_SYNC1, PROC_STATE_SYNC0};
	uint8_t outputBuffer[MAX_DATA_LEN];
	
	while (1) {
		// count = CBufRead(pkg->streamIn, (char *)&buffer, 1);
		if (pkg->readInput)
		{
			count = pkg->readInput((char *)&buffer, 1);
		}
		else
		{
			count = 0;
		}
		
		if (count == 0)
		{
			if (pkg->state > PROC_STATE_SYNC1)
			{
				pkg->timeout --;
				
				if (pkg->timeout == 0)
				{
					DBG("timeout");
					wait4sync();
				}
			}
			break;
		}
		else
		{
			pkg->timeout = TIMEOUT_ROUNDS;
		}
		
		if (pkg->state == PROC_STATE_SYNC0)
		{
			DBG("sync0");
			if (buffer == SYNC_HEADER0)
			{
				pkg->state = PROC_STATE_SYNC1;
			}
			else
			{
				wait4sync();
			}
		}
		else if (pkg->state == PROC_STATE_SYNC1)
		{
			DBG("sync1");
			if (buffer == SYNC_HEADER1)
			{
				DBG("start");
				pkg->state = PROC_STATE_INDEX;
			}
			else
			{
				wait4sync();
			}
		}
		else if (pkg->state == PROC_STATE_INDEX)
		{
			if (buffer >= U2WRQ_ILLEGAL)
			{
				DBG("illegal index: %d", buffer);
				wait4sync();
			}
			else
			{
				pkg->cmdIndex = buffer;
				pkg->state = PROC_STATE_NOD;
				DBG("index: %d", pkg->cmdIndex);
			}
		}
		else if (pkg->state == PROC_STATE_NOD)
		{
			if (buffer > MAX_DATA_LEN)
			{
				DBG("data overflow: %d", buffer);
				wait4sync();
			}
			else
			{
				pkg->cmdDataLen = buffer;
				pkg->cmdDataIndex = 0;
				
				if (pkg->cmdDataLen)
				{
					pkg->state = PROC_STATE_DATA;
				}
				else
				{
					pkg->state = PROC_STATE_CRC;
				}
				DBG("data length: %d", pkg->cmdDataLen);
			}
		}
		else if (pkg->state == PROC_STATE_DATA)
		{
			DBG("data: 0x%02x", buffer);
			pkg->cmdData[pkg->cmdDataIndex ++] = buffer;
			if (pkg->cmdDataIndex == pkg->cmdDataLen)
			{
				pkg->state = PROC_STATE_CRC;
			}
		}
		else if (pkg->state == PROC_STATE_CRC)
		{
			pkg->crcValue = buffer;
			if (pkg->crcValue == crc7(pkg->cmdIndex, pkg->cmdData, pkg->cmdDataLen))
			{
				DBG("crc match");
				count = processCommand(pkg->cmdIndex, pkg->cmdData, outputBuffer, pkg->cmdDataLen);
				
				if (count)
				{
					outputBuffer[count] = crc7(crc7(pkg->cmdIndex, &count, 1), outputBuffer, count);
				}
				else
				{
					outputBuffer[0] = crc7(pkg->cmdIndex, &count, 1);
				}
				if (pkg->writeOutput)
				{
					pkg->writeOutput((const char *)sync, 2);
					pkg->writeOutput((const char *)&pkg->cmdIndex, 1);
					pkg->writeOutput((const char *)&count, 1);
					pkg->writeOutput((const char *)outputBuffer, count + 1);
				}
			}
			else
			{
				DBG("crc error: e[0x%02x] g[0x%02x]",
					crc7(pkg->cmdIndex, pkg->cmdData, pkg->cmdDataLen) & 0xFF, pkg->crcValue & 0xFF);
			}
			wait4sync();
		}
		else {
			wait4sync();
		}
	}
}

//
// stream format:
// uint16 value0 + uint16 value1 + data x n
//
static int processCommand(uint8_t index, uint8_t *input, uint8_t *output, uint8_t data_len)
{
	cmd_word_t value0, value1;
	const char *buffer = (const char *)(input + 4);
	
	// command address
	memcpy(&value0.word, &input[0], 2);
	// command length
	memcpy(&value1.word, &input[2], 2);
	data_len -= 4;
	
	DBG("value0 = 0x%x", value0.word & 0xFFFF);
	DBG("value1 = 0x%x", value1.word & 0xFFFF);
	
	if (index == U2WRQ_RADIO_WRITE)
	{
		// return CBufWrite(&radioTxBuf, input, data_len);
		DBG("radio write");
		return 0;
	}
	else if (index == U2WRQ_RADIO_READ)
	{
		// return CBufRead(&radioRxBuf, output, input[0]);
		DBG("radio read");
		return 0;
	}
	else if (index == U2WRQ_MCU_RESET)
	{
		//
		// reset avr device
		//
		DBG("reset device");
		if ((value0.word == 0x1985) && (value1.word == 0x0709))
		{
			wdt_enable(WDTO_15MS);
			while (1);
		}
		// never gets here
		return 0;
	}
	else if (index == U2WRQ_FLASH_WRITE)
	{
		//
		// write flash data
		//
		DBG("write flash");
		return 0;
	}
	else if (index ==U2WRQ_FLASH_READ)
	{
		//
		// read flash data
		//
		DBG("read flash");
		return 0;
	}
	else if (index == U2WRQ_EEPROM_WRITE)
	{
		//
		// write eeprom data
		//
		DBG("eeprom write");
		eepromWrite((const char *)buffer, value0.word, value1.word);
		return 0;
	}
	else if (index ==U2WRQ_EEPROM_READ)
	{
		//
		// read eeprom data
		//
		DBG("eeprom read");
		return eepromRead((char *)output, value0.word, value1.word);
	}
	else if (index == U2WRQ_MEM8_RW)
	{
		//
		// read/write avr register
		//
		DBG("r/w mcu register");
		if (value0.bytes[1])
		{
			_SFR_MEM8(value0.bytes[0]) = value1.bytes[0];
		}
		output[0] = _SFR_MEM8(value0.bytes[0]);
		return 1;
	}
	else if (index == U2WRQ_MEM8_BIT)
	{
		//
		// set/clear avr register
		//
		DBG("set/clear mcu register");
		if (value0.bytes[1])
		{
			_SFR_MEM8(value0.bytes[0]) |= value1.bytes[0];
		}
		else
		{
			_SFR_MEM8(value0.bytes[0]) &= ~(value1.bytes[0]);
		}
		output[0] = _SFR_MEM8(value0.bytes[0]);
		return 1;
	}
	else if (index == U2WRQ_IR_CONFIG)
	{
		//
		// write IR config data
		//
		DBG("write IR configure");
		IRCodingConfig(buffer, value0.word);
		return 0;
	}
	else if (index == U2WRQ_IR_WDATA)
	{
		//
		// write IR data
		// DBG("wir %d", usbMessage.dataLength);
		DBG("write IR data");
		IRUSBWrite(buffer, value0.word);
		return 0;
	}
	else if (index == U2WRQ_READ_TEMP)
	{
		//
		// read temperature
		// fixed transfer length 2 bytes
		//
		DBG("read temp");
		output[0] = Temperature[0];
		output[1] = Temperature[1];
		return 2;
	}
	else if (index == U2WRQ_IDLE)
	{
		//
		// do nothing for idle command
		//
		DBG("idle");
		return 0;
	}
	return 0;
}

#if (0)
static proc_package_t uartCmd = {0};
static void delay(volatile unsigned int t)
{
	while (t --);
}
int writeUart(uint8_t *buffer, int length)
{
	int i;
	
	for(i = 0; i < length; i ++)
	{
		uart1_putc(buffer[i]);
	}
	return length;
}

void cmdMain() {
	uartCmd.readInput = (void *)uartRead;
	uartCmd.writeOutput = writeUart;
	uartCmd.state = PROC_STATE_SYNC0;
	
	DBG(" -> program start <- ");
	while (1)
	{
		delay(0xFFF);
		processCommandStream(&uartCmd);
	}
}
#endif
